package com.hxzy.projectssmplus.security;

import com.hxzy.projectssmplus.conf.properties.HxzyProperties;
import com.hxzy.projectssmplus.util.GlobalConst;
import com.hxzy.projectssmplus.util.IpUtils;
import com.hxzy.projectssmplus.util.ServletUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 令牌管理类
 * 1)、HttpServletRequest取得令牌
 * 2)、jwt来进行解码
 * 3)、redis查询uuid是否存在
 * 4)、jwt进行加密
 * 5)、redis保存uuid信息
 */
@Component
public class TokenService {

    @Autowired
    private RedisTemplate  redisTemplate;

    @Autowired
    private HxzyProperties hxzyProperties;

    /**
     * 根据请求，取得token，验证token是否有效
     * @param request
     * @return
     */
    public LoginUserDetails getLoginUserDetails(HttpServletRequest request) {

        // 获取请求携带的令牌(jwt加密的uuid)
        String token = request.getHeader("X-Token");
        if(StringUtils.isNotBlank(token)){

            //检查jwt串是否有效(做了全局异常处理的)
            Claims body = Jwts.parser().setSigningKey(this.hxzyProperties.getSecurity()).parseClaimsJws(token).getBody();
            String uuid=body.getSubject();
            String redisKey=GlobalConst.LOGIN_REDIS_PREF+uuid;
            //读取值
            Object redisValue=this.redisTemplate.opsForValue().get(redisKey);
            if(redisValue==null){
                return null;
            }

           LoginUserDetails loginUserDetails= (LoginUserDetails) redisValue;
            return loginUserDetails;
        }

        return null;
    }


    /**
     * 登录成功以后，创建令牌
     * 1)、生成uuid
     * 2)、设定自定义loginUserDetails中的uuid,创建时间，过期时间
     * 3）、写入到redis中 uuid --> LoginUserDetails类，设定一个有效期
     * 4)、再把uuid使用jwt加密返回字符串
     * 5)、返回加密后的uuid的值
     * @param loginUserDetails
     * @return
     */
    public String createToken(LoginUserDetails loginUserDetails) {
        //1、生成uuid
        String uuid= UUID.randomUUID().toString();
        //2、设定自定义loginUserDetails中的uuid,创建时间，过期时间,ip
        loginUserDetails.setToken(uuid);
        String ip= IpUtils.getIpAddr(ServletUtil.getReqeust());
        loginUserDetails.setIpaddr(ip);

        //写入到redis中
        refreshToken(loginUserDetails);

        //把uuid变为jwt串
        String token= jwtCreateToken(uuid);

        return token;
    }

    /**
     * 写入到redis中 uuid --> LoginUserDetails类，设定一个有效期
     * @param loginUserDetails
     */
    public void refreshToken(LoginUserDetails loginUserDetails){
        long beginTime=System.currentTimeMillis();
        loginUserDetails.setLoginTime(beginTime);
        loginUserDetails.setExpireTime(beginTime+ GlobalConst.LOGI_REDIS_EXPIRE_SECONDS*1000L);
        // 根据uuid将loginUser缓存
        String redisKey=GlobalConst.LOGIN_REDIS_PREF+loginUserDetails.getToken();
        this.redisTemplate.opsForValue().set(redisKey, loginUserDetails, GlobalConst.LOGI_REDIS_EXPIRE_SECONDS,TimeUnit.SECONDS);
    }

    /**
     * 把令牌使用jwt来加密
     * @param token
     * @return
     */
    public String jwtCreateToken(String token){
        JwtBuilder builder= Jwts.builder()
                .setId(UUID.randomUUID().toString())   //ID
                .setSubject(token)                  //内容
                .signWith(SignatureAlgorithm.HS256,this.hxzyProperties.getSecurity());  //加密密钥

        //生成加密后的字符串
       return builder.compact();
    }

    /**
     * 根据uuid删除redis中的值
     * @param token
     */
    public void delLoginUser(String token) {
        String redisKey=GlobalConst.LOGIN_REDIS_PREF+token;
        this.redisTemplate.delete(redisKey);
    }

    /**
     * 为uuid判断它的过期时间是否<15分钟，如果是重新为它分配时间
     * @param loginUser
     */
    public void verifyToken(LoginUserDetails loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        //过期时间<=15分钟
        Long MILLIS_MINUTE_TEN = 15 * 60 * 1000L;
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }
}

